﻿using System;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using XFControls.Utils;
using System.Reflection;

#pragma warning disable 1591
// ReSharper disable InconsistentNaming
namespace XFControls.XFPanels.XFPanelItems
{
    public class XFItemBase : IXFItem
    {
        private object _invalThreadLock = new object();
        private int _width;
        private int _widthScrollCompensate;
        private Thread _invalThread;

        #region IXFItem Members

        public event EventHandler<EventArgs> Click;
        public event EventHandler<EventArgs> Change;
        public event EventHandler<EventArgs> ScreenResize;
        public event EventHandler<EventArgs> MouseUp;
        public event EventHandler<MouseEventArgs> MouseDown;
        public event EventHandler<MouseEventArgs> MouseMove;
        public event EventHandler<EventArgs> Focus;
        public event EventHandler<EventArgs> Blur;
        public event EventHandler<EventArgs> Refresh;
        public event EventHandler<EventArgs> Disposed;
        public event EventHandler<EventArgs> Draw;
        public event EventHandler<EventArgs> Add;
        public XFPanelBase Parent { get; set; }
        public XFItemState State { get; set; }
        public Bitmap Buffer { get; set; }
        public XFItemStyle Style { get; set; }
        public XFItemType ItemType { get; set; }
        public bool Visible { get; set; }
        public bool Animated { get; set; }
        public bool Rendered { get; set; }
        public Rectangle ItemRectangle { get; set; }
        public object Tag { get; set; }

        protected Color _backColor;

        public XFItemBase()
        {
            Visible = true;
            Animated = true;
            ItemType = XFItemType.Display;
        }

        public virtual void ItemPaint(Graphics g, int x, int y)
        {
            if (Draw != null)
                Draw(this, new EventArgs());

            if (Style != null && Style.BackgroundColor == Color.Empty && !Parent.InvokeRequired)
                Style.BackgroundColor = Parent.BackColor;

            switch (State)
            {
                case XFItemState.Normal:
                    g.DrawBackground(this, Style, x, y, GetWidth(false), GetHeight());
                    break;
                case XFItemState.Selected:
                    g.DrawBackgroundSelected(this, Style, x, y, GetWidth(false), GetHeight());
                    break;
                case XFItemState.Disabled:
                    g.DrawBackgroundDisabled(this, Style, x, y, GetWidth(false), GetHeight());
                    break;
            }
            g.DrawBorders(Style, x, y, GetWidth(), GetHeight());
        }

        public virtual int GetHeight()
        {
            throw new NotImplementedException();
        }

        public virtual int GetWidth()
        {
            return GetWidth(true);
        }

        public int GetWidth(bool scrollBarCompensate)
        {
            var width = Screen.PrimaryScreen.WorkingArea.Width;

            if (Parent != null)
            {
                width = scrollBarCompensate ? _widthScrollCompensate : _width;
            }

            return width;
        }

        public virtual void ResetHeight() { }

        public virtual XFItemClickResult GetClickResult()
        {
            OnClick();
            return XFItemClickResult.Nothing;
        }

        public virtual void OnClick()
        {
            if (Click != null)
                Click(this, new EventArgs());
        }

        public virtual void OnChange()
        {
            if (Change != null)
                Change(this, new EventArgs());
        }

        public virtual void OnScreenResize()
        {
            if (ScreenResize != null)
                ScreenResize(this, new EventArgs());
        }

        public virtual void OnMouseUp()
        {
            if (MouseUp != null)
                MouseUp(this, new EventArgs());
        }

        public virtual void OnMouseDown(int x, int y)
        {
            if (MouseDown != null)
                MouseDown(this, new MouseEventArgs(MouseButtons.Left, 1, x, y, 0));
        }

        public virtual void OnMouseMove(int x, int y)
        {
            if (MouseMove != null)
                MouseMove(this, new MouseEventArgs(MouseButtons.Left, 1, x, y, 0));
        }

        public virtual void OnFocus()
        {
            if (Focus != null)
                Focus(this, new EventArgs());
        }

        public virtual void OnBlur()
        {
            if (Blur != null)
                Blur(this, new EventArgs());
        }

        public virtual void OnRefresh()
        {
            if (Refresh != null)
                Refresh(this, new EventArgs());
        }

        public virtual void OnAdd()
        {
            modifyWidth(ref _width, false);
            modifyWidth(ref _widthScrollCompensate, true);

            if (Add != null)
                Add(this, new EventArgs());
        }

        public IXFItem GetCopy()
        {
            IXFItem clone = (IXFItem)MemberwiseClone();
            clone.Style = clone.Style.Clone() as XFItemStyle;
            return clone;
        }

        #endregion

        public void Reset()
        {
            if (Buffer == null) return;

            lock (Buffer)
            {
                Buffer.Dispose();
                Buffer = null;
                ResetHeight();
            }
        }

        public Bitmap GetImageUI(string uri)
        {
            if (XFGraphics.ImageCache.ContainsKey(uri))
            {
                var val = XFGraphics.ImageCache[uri];
                if (File.Exists(val))
                {
                    var reader = File.OpenRead(XFGraphics.ImageCache[uri]);
                    return XFBitmapFactory.GetBitmap(reader);
                }
            }

            var myAssembly = GetType().Assembly;
            string ns = myAssembly.GetName().Name;
            var ms = (MemoryStream)myAssembly.GetManifestResourceStream(ns + ".Resources." + uri + ".png");
            if (ms == null)
            {
                myAssembly = Assembly.GetExecutingAssembly();
                ns = myAssembly.GetName().Name;
                ms = (MemoryStream)myAssembly.GetManifestResourceStream(ns + ".Resources." + uri + ".png");
                if (ms == null) return null;
            }

            return XFBitmapFactory.GetBitmap(ms);
        }

        public void DrawUI(string uri, Graphics g, Rectangle rec)
        {
            DrawUI(uri, g, rec, true);
        }

        public void DrawUI(string uri, Graphics g, Rectangle rec, bool maintainImgRatio)
        {
            byte[] buffer = null;

            if (XFGraphics.ImageCache.ContainsKey(uri))
            {
                var val = XFGraphics.ImageCache[uri];
                if (File.Exists(val))
                {
                    var reader = File.OpenRead(XFGraphics.ImageCache[uri]);
                    buffer = new byte[reader.Length];
                    reader.Read(buffer, 0, buffer.Length);
                }
            }
            if (buffer == null)
            {
                var myAssembly = GetType().Assembly;
                string ns = myAssembly.GetName().Name;
                var ms = (MemoryStream)myAssembly.GetManifestResourceStream(ns + ".Resources." + uri + ".png");
                if (ms == null)
                {
                    myAssembly = Assembly.GetExecutingAssembly();
                    ns = myAssembly.GetName().Name;
                    ms = (MemoryStream)myAssembly.GetManifestResourceStream(ns + ".Resources." + uri + ".png");
                    if (ms == null) return;
                }

                buffer = ms.GetBuffer();

                if (buffer == null) return;
                if (Parent != null) Parent.ImageCache.Add(buffer, uri);
            }

            XFGraphics.DrawMemoryStream(g, rec, buffer, maintainImgRatio);
        }

        protected void DrawIcon(string uri, Graphics g, Rectangle rec)
        {
            byte[] ms = null;

            ms = GetImageCache(uri, ms, rec) ?? Parent.ImageCache[uri];

            if (ms == null && Parent.LeftPanel != null)
                ms = Parent.LeftPanel.ImageCache[uri];

            if (ms == null && Parent.RightPanel != null)
                ms = Parent.RightPanel.ImageCache[uri];

            if (ms == null)
            {
                DrawUI("iconThumb", g, rec);

                XFGraphics.AddToList(this, uri, stream =>
                {
                    var array = stream.GetBuffer();
                    if (array == null || array.Length == 0) return;
                    XFGraphics.CacheImage(uri, array);

                    if (Buffer == null) return;
                    lock (Buffer)
                    {
                        using (var gr = Graphics.FromImage(Buffer))
                            XFGraphics.DrawMemoryStream(gr, rec, array);
                    }

                    Invalidate(true);
                });

                return;
            }

            XFGraphics.DrawMemoryStream(g, rec, ms);
        }

        private byte[] GetImageCache(string uri, byte[] ms, Rectangle rec)
        {
            if (!XFGraphics.ImageCache.ContainsKey(uri)) return ms;

            if (!File.Exists(XFGraphics.ImageCache[uri])) return ms;


            for (int i = 0; i < 5; i++)
            {
                try
                {
                    using (var file = File.Open(XFGraphics.ImageCache[uri], FileMode.Open))
                    {
                        ms = new byte[file.Length];
                        file.Read(ms, 0, (int)file.Length);
                    }
                    break;
                }
                catch (IOException) { Thread.Sleep(100); }

            }

            XFGraphics.AddToList(this, uri, stream =>
            {
                var array = stream.GetBuffer();
                XFGraphics.CacheImage(uri, array);

                if (Buffer == null) return;
                lock (Buffer)
                {
                    using (var gr = Graphics.FromImage(Buffer))
                        XFGraphics.DrawMemoryStream(gr, rec, array);
                }
            });

            return ms;
        }

        protected void DrawImage(string uri, Graphics g, Rectangle rec)
        {
            byte[] ms = null;

            ms = GetImageCache(uri, ms, rec) ?? Parent.ImageCache[uri];

            if (ms == null && Parent.LeftPanel != null)
                ms = Parent.LeftPanel.ImageCache[uri];

            if (ms == null && Parent.RightPanel != null)
                ms = Parent.RightPanel.ImageCache[uri];

            if (ms == null)
            {
                DrawUI("image", g, rec);

                XFGraphics.AddToList(this, uri, stream =>
                {
                    var array = stream.GetBuffer();
                    if (array == null || array.Length == 0) return;
                    XFGraphics.CacheImage(uri, array);

                    if (Buffer == null) return;
                    lock (Buffer)
                    {
                        using (var gr = Graphics.FromImage(Buffer))
                            XFGraphics.DrawMemoryStream(gr, rec, array);
                    }
                    Invalidate(true);
                });

                return;
            }

            XFGraphics.DrawMemoryStream(g, rec, ms);
        }

        protected Font getCurrentFont(bool secondary)
        {
            switch (State)
            {
                case XFItemState.Normal:
                    return secondary ? Style.SecondaryTextFont : Style.TextFont;
                case XFItemState.Selected:
                    if ((Style.SelectedTextFont != null && !secondary) || (Style.SelectedSecondaryTextFont != null && secondary))
                        return secondary ? Style.SelectedSecondaryTextFont : Style.SelectedTextFont;

                    return secondary ? Style.SecondaryTextFont : Style.TextFont;
                case XFItemState.Disabled:
                    if ((Style.DisabledTextFont != null && !secondary) || (Style.DisabledSecondaryTextFont != null && secondary))
                        return secondary ? Style.DisabledSecondaryTextFont : Style.DisabledTextFont;

                    return secondary ? Style.SecondaryTextFont : Style.TextFont;
            }
            return secondary ? Style.SecondaryTextFont : Style.TextFont;
        }

        protected Color getCurrentFontColor(bool secondary)
        {
            switch (State)
            {
                case XFItemState.Normal:
                    return secondary ? Style.SecondaryTextColor : Style.TextColor;
                case XFItemState.Selected:
                    if ((Style.SelectedTextColor != Color.Empty && !secondary) || (Style.SelectedSecondaryTextColor != Color.Empty && secondary))
                        return secondary ? Style.SelectedSecondaryTextColor : Style.SelectedTextColor;

                    return secondary ? Style.SecondaryTextColor : Style.TextColor;
                case XFItemState.Disabled:
                    if ((Style.DisabledTextColor != Color.Empty && !secondary) || (Style.DisabledSecondaryTextColor != Color.Empty && secondary))
                        return secondary ? Style.DisabledSecondaryTextColor : Style.DisabledTextColor;

                    return secondary ? Style.SecondaryTextColor : Style.TextColor;
            }
            return secondary ? Style.SecondaryTextColor : Style.TextColor;
        }

        private void modifyWidth(ref int width, bool scrollBarCompensate)
        {
            if (Parent == null || Parent.InvokeRequired) return;
            width = Parent.Width;
            if (Parent.AlphaBarVisible)
                width -= Parent.AlphaBarWidth;

            if (Parent.ScrollbarVisible && scrollBarCompensate)
                width -= 5;
        }

        public void Invalidate()
        {
            modifyWidth(ref _width, false);
            modifyWidth(ref _widthScrollCompensate, true);

            Invalidate(false);
        }

        public virtual void Deflate()
        {
            if(Buffer != null)
                Buffer.Dispose();
            Buffer = null;
        }

        public virtual void Invalidate(bool invalParent)
        {
            if (Buffer == null) return;
            Invalidate(invalParent, ThreadPriority.BelowNormal);
        }

        public virtual void Invalidate(bool invalParent, ThreadPriority priority)
        {
            if (Buffer == null) return;

            ThreadStart start = () =>
            {
                lock (_invalThreadLock)
                    try
                    {
                        Thread.CurrentThread.Priority = priority;
                        int height = GetHeight();

                        if (height == 0) return;
                        using (var temp = XFBitmapFactory.GetBitmap(_width, height))
                        using (Graphics g = Graphics.FromImage(temp))
                        {
                            ItemPaint(g, 0, 0);
                            if (Buffer == null) return;
                            lock (Buffer)
                            {
                                using (Graphics buff = Graphics.FromImage(Buffer))
                                    buff.DrawImage(temp, 0, 0);
                            }
                        }

                        if (invalParent && !Parent.IsDisposed)
                            Parent.Damage();
                    }
                    catch { }
            };

            _invalThread = new Thread(start)
                           {
                               IsBackground = true,
                               Priority = priority
                           };

            _invalThread.Start();
        }

        public virtual void Dispose()
        {
            if (Buffer != null)
                Buffer.Dispose();

            Buffer = null;

            if (Style != null)
                Style.Dispose();

            Style = null;

            if (Disposed != null)
                Disposed(this, new EventArgs());

            Click = null;
            Change = null;
            ScreenResize = null;
            MouseUp = null;
            MouseDown = null;
            MouseMove = null;
            Focus = null;
            Blur = null;
            Refresh = null;
            Disposed = null;
        }
    }

    public class XFItemExpander : XFItemBase, IXFItemExpandable
    {
        private readonly int _min = 30;
        private int _height;
        private int _textHeight;
        private bool _metThreshold;

        public string Description;
        public string PreThresholdText;
        public string PostThresholdText;
        public int Threshold;
        public int Max;
        public bool Enabled = true;


        public event EventHandler ReleasePostThreshold;

        public XFItemExpander()
        {
            Style = new XFItemStyle
                        {
                            SelectedBackgroundAlign = XFItemBackgroundAlign.Center,
                            BackgroundColor = Color.White,
                            Padding = 25,
                            PaddingBottom = 45,
                            TextFont = new Font(FontFamily.GenericSansSerif, 10, FontStyle.Regular)
                        };

            ItemType = XFItemType.Display;

            if (XFGraphics.GetScreenResolutionNotRotated() == ScreenResolution.QVGA) _min /= 2;
            _height = _min;
        }

        public override void OnAdd()
        {
            base.OnAdd();
            Parent.MouseUp += Parent_MouseUp;
        }

        void Parent_MouseUp(object sender, MouseEventArgs e)
        {
            if (!Enabled) return;
            if (_metThreshold && ReleasePostThreshold != null)
                ReleasePostThreshold(this, new EventArgs());

            while (_height > _min + 2)
            {
                if (Parent.IsDisposed) return;

                int dif = _height - _min;
                dif /= 2;

                _height -= dif;

                if (Buffer != null)
                    lock (Buffer)
                    {
                        Buffer.Dispose();
                        Buffer = null;
                    }
                Rendered = false;

                if (Parent is XFPanelList)
                    ((XFPanelList)Parent).Resize();

                Parent.Invalidate();
                Application.DoEvents();
            }
            _height = _min;
        }

        public void SetHeight(int ammount)
        {
            if (!Enabled) return;
            if (ammount > Max)
            {
                _height = Max;
                return;
            }
            if (ammount < _min)
            {
                _height = _min;
                return;
            }

            if (Style == null)
                return;

            var rec = new Rectangle(0, 0, GetWidth() - (Style.PaddingLeft + Style.PaddingRight), 0);

            if (ammount > Threshold && !_metThreshold)
            {
                _textHeight = Style.TextFont.MeasureString(PostThresholdText, rec).Height;
                _metThreshold = true;
            }

            if (ammount < Threshold && _metThreshold)
            {
                _textHeight = Style.TextFont.MeasureString(PreThresholdText, rec).Height;
                _metThreshold = false;
            }

            _height = ammount;
            if (Buffer != null)
                lock (Buffer)
                {
                    Buffer.Dispose();
                    Buffer = null;
                }

            Rendered = false;
        }

        public override int GetHeight()
        {
            return _height;
        }

        public override void ItemPaint(Graphics g, int x, int y)
        {
            base.ItemPaint(g, x, y);
            int width = 12;
            int bottomPadding = 5;
            int height = 25;

            var rec = new Rectangle(0, 0, GetWidth() - (Style.PaddingLeft + Style.PaddingRight), 0);
            if (_textHeight == 0)
                _textHeight = Style.TextFont.MeasureString(PreThresholdText, rec).Height;

            if (XFGraphics.GetScreenResolutionNotRotated() == ScreenResolution.QVGA)
            {
                height /= 2;
                bottomPadding /= 2;
            }

            var imgRec = new Rectangle(0, _height - bottomPadding - height, GetWidth(), height);
            rec = new Rectangle(Style.PaddingLeft, _height - (_textHeight + Style.PaddingBottom), GetWidth() - (Style.PaddingLeft + Style.PaddingRight), 0);

            if (string.IsNullOrEmpty(Description))
                DrawUI("refresh", g, imgRec);
            else
                g.DrawString(Description, imgRec, Style);


            g.DrawString(_height < Threshold ? PreThresholdText : PostThresholdText, rec, Style);
        }

        public override void Dispose()
        {
            ReleasePostThreshold = null;
            base.Dispose();
        }
    }

}
// ReSharper restore InconsistentNaming
#pragma warning restore 1591
